﻿namespace ImageProcessing.IMG_Processor
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// color struct
    /// </summary>
    public struct IMG_color
    {
        /// <summary>
        /// Red component
        /// </summary>
        public float Red;

        /// <summary>
        /// Green component
        /// </summary>
        public float Green;

        /// <summary>
        /// Blue component
        /// </summary>
        public float Blue;
    }

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public static class IMG_HistogramOp
    {
        /// <summary>
        /// array of image red values for the histogram
        /// </summary>
        private static int[] imgRedValues = new int[256];

        /// <summary>
        /// array of image green values for the histogram
        /// </summary>
        private static int[] imgGreenValues = new int[256];

        /// <summary>
        /// array of image blue values for the histogram
        /// </summary>
        private static int[] imgBlueValues = new int[256];

        /// <summary>
        /// array of image grey values for the histogram
        /// </summary>
        private static int[] imgGrayScale = new int[256];

        /// <summary>
        /// values used in normalization
        /// </summary>
        private static int imgMinR, imgMinG, imgMinB, imgMaxR, imgMaxG, imgMaxB;

        /// <summary>
        /// Gets or sets red values of histogram
        /// </summary>
        public static int[] ImgRedValues
        {
            get { return imgRedValues; }
            set { imgRedValues = value; }
        }

        /// <summary>
        /// Gets or sets green values of histogram
        /// </summary>
        public static int[] ImgGreenValues
        {
            get { return imgGreenValues; }
            set { imgGreenValues = value; }
        }

        /// <summary>
        /// Gets or sets blue values of histogram
        /// </summary>
        public static int[] ImgBlueValues
        {
            get { return imgBlueValues; }
            set { imgBlueValues = value; }
        }

        /// <summary>
        /// Gets or sets green values of histogram
        /// </summary>
        public static int[] ImgGrayScale
        {
            get { return imgGrayScale; }
            set { imgGrayScale = value; }
        }

        /// <summary>
        /// contrast functon
        /// </summary>
        /// <param name="img_oldImg">old image buffer</param>
        /// <param name="img_newMin">new min value</param>
        /// <param name="img_newMax">nue max value</param>
        /// <returns>buffer of contrast image</returns>
        public static Entities.IMG_Image IMG_Contrast(Entities.IMG_Image img_oldImg, int img_newMin, int img_newMax)
        {
            try
            {
                int img_Width = img_oldImg.Width;
                int img_Height = img_oldImg.Height;

                Entities.IMG_Image img_NewImg = new Entities.IMG_Image(img_Width, img_Height);
                
                int index = img_Width * img_Height;
                double minR = double.MaxValue;
                double maxR = double.MinValue;
                double minG = double.MaxValue;
                double maxG = double.MinValue;
                double minB = double.MaxValue;
                double maxB = double.MinValue;
                Entities.IMG_Image retImg = new Entities.IMG_Image(img_Width, img_Height);
                for (int i = 0; i < index; i++)
                {
                    minR = minR < img_oldImg.ImagePixels[i].Red ? minR : img_oldImg.ImagePixels[i].Red;
                    maxR = maxR > img_oldImg.ImagePixels[i].Red ? maxR : img_oldImg.ImagePixels[i].Red;
                    minG = minG < img_oldImg.ImagePixels[i].Green ? minG : img_oldImg.ImagePixels[i].Green;
                    maxG = maxG > img_oldImg.ImagePixels[i].Green ? maxG : img_oldImg.ImagePixels[i].Green;
                    minB = minB < img_oldImg.ImagePixels[i].Blue ? minB : img_oldImg.ImagePixels[i].Blue;
                    maxB = maxB > img_oldImg.ImagePixels[i].Blue ? maxB : img_oldImg.ImagePixels[i].Blue;
                }

                for (int i = 0; i < index; i++)
                {
                    int newR, newG, newB;
                    newR = (int)(((img_oldImg.ImagePixels[i].Red - minR) / (double)(maxR - minR)) * (img_newMax - img_newMin)) + img_newMin;
                    newG = (int)(((img_oldImg.ImagePixels[i].Green - minG) / (double)(maxG - minG)) * (img_newMax - img_newMin)) + img_newMin;
                    newB = (int)(((img_oldImg.ImagePixels[i].Blue - minB) / (double)(maxB - minB)) * (img_newMax - img_newMin)) + img_newMin;

                    newR = newR >= 0 ? newR : 0;
                    newG = newG >= 0 ? newG : 0;
                    newB = newB >= 0 ? newB : 0;

                    newR = newR <= 255 ? newR : 255;
                    newG = newG <= 255 ? newG : 255;
                    newB = newB <= 255 ? newB : 255; 

                    img_NewImg.ImagePixels[i] = new Entities.Pixel(newR, newG, newB);
                }

                return img_NewImg;
            }
            catch (Exception img_ex)
            {
                Logger.LogException(img_ex);
                throw;
            }
        }

        /// <summary>
        /// Gamma function
        /// </summary>
        /// <param name="img_oldImg">old image buffer</param>
        /// <param name="img_Gamma">gamma value</param>
        /// <returns>a new buffer of the image</returns>
        public static Entities.IMG_Image IMG_Gamma(Entities.IMG_Image img_oldImg, double img_Gamma)
        {
            try
            {
                int img_Width = img_oldImg.Width;
                int img_Height = img_oldImg.Height;

                Entities.IMG_Image img_NewImg = new Entities.IMG_Image(img_Width, img_Height);
                float newR, newG, newB;
                int index = img_Width * img_Height;
                for (int i = 0; i < index; i++)
                {
                    newR = (float)Math.Pow(img_oldImg.ImagePixels[i].Red, img_Gamma);
                    newB = (float)Math.Pow(img_oldImg.ImagePixels[i].Blue, img_Gamma);
                    newG = (float)Math.Pow(img_oldImg.ImagePixels[i].Green, img_Gamma);
                    img_NewImg.ImagePixels[i] = new Entities.Pixel(newR, newG, newB);
                }

                return ImageProcessor.IMG_Normalize.IMG_NormalizeImage(img_NewImg);
            }
            catch (Exception img_ex)
            {
                Logger.LogException(img_ex);
                throw;
            }
        }

        /// <summary>
        /// calculates histogram values
        /// </summary>
        /// <param name="img_OrigionalImg">img buffer</param>
        public static void IMG_CalculateHistogramValues(Entities.IMG_Image img_OrigionalImg)
        {
            try
            {
                int img_HeightR = 256;
                int img_HeightG = 256;
                int img_HeightB = 256;
                Array.Clear(ImgRedValues, 0, img_HeightR);
                Array.Clear(ImgGreenValues, 0, img_HeightG);
                Array.Clear(ImgBlueValues, 0, img_HeightB);
                int index = img_OrigionalImg.Width * img_OrigionalImg.Height;
                Entities.IMG_Image img_GraySImg = IMG_Processor.IMG_GrayScale.IMG_GrayScaleBuffer(img_OrigionalImg);
                int x;
                for (int i = 0; i < index; i++)
                {
                    x = (int)img_OrigionalImg.ImagePixels[i].Red;
                    imgRedValues[x]++;
                    imgMinR = imgMinR < x ? imgMinR : x;
                    imgMaxR = imgMaxR > x ? imgMaxR : x;
                    x = (int)img_OrigionalImg.ImagePixels[i].Green;
                    imgGreenValues[x]++;
                    imgMinG = imgMinG < x ? imgMinG : x;
                    imgMaxG = imgMaxG > x ? imgMaxG : x;
                    x = (int)img_OrigionalImg.ImagePixels[i].Blue;
                    imgBlueValues[x]++;
                    imgMinB = imgMinB < x ? imgMinB : x;
                    imgMaxB = imgMaxB > x ? imgMaxB : x;
                    imgGrayScale[(int)img_GraySImg.ImagePixels[i].Red]++;
                }
            }
            catch (Exception img_ex)
            {
                Logger.LogException(img_ex);
                throw;
            }
        }

        /// <summary>
        /// brightness function
        /// </summary>
        /// <param name="img_OldBuffer">old image buffer</param>
        /// <param name="img_Offset">offset value</param>
        /// <returns>a new buffer of the image</returns>
        public static Entities.IMG_Image IMG_BrightnessBuffer(Entities.IMG_Image img_OldBuffer, int img_Offset)
        {
            try
            {
                Entities.IMG_Image img_NewBuffer = new Entities.IMG_Image(img_OldBuffer.Width, img_OldBuffer.Height);
                int index = img_OldBuffer.Width * img_OldBuffer.Height;
                for (int i = 0; i < index; i++)
                {
                    if (img_Offset > 0)
                    {
                        img_NewBuffer.ImagePixels[i] = new Entities.Pixel(
                            ((img_OldBuffer.ImagePixels[i].Red + img_Offset) > 255) ? 255 : (img_OldBuffer.ImagePixels[i].Red + img_Offset),
                            ((img_OldBuffer.ImagePixels[i].Green + img_Offset) > 255) ? 255 : (img_OldBuffer.ImagePixels[i].Green + img_Offset),
                            ((img_OldBuffer.ImagePixels[i].Blue + img_Offset) > 255) ? 255 : (img_OldBuffer.ImagePixels[i].Blue + img_Offset));
                    }
                    else
                    {
                        img_NewBuffer.ImagePixels[i] = new Entities.Pixel(
                            ((img_OldBuffer.ImagePixels[i].Red + img_Offset) < 0) ? 0 : (img_OldBuffer.ImagePixels[i].Red + img_Offset),
                            ((img_OldBuffer.ImagePixels[i].Green + img_Offset) < 0) ? 0 : (img_OldBuffer.ImagePixels[i].Green + img_Offset),
                            ((img_OldBuffer.ImagePixels[i].Blue + img_Offset) < 0) ? 0 : (img_OldBuffer.ImagePixels[i].Blue + img_Offset));
                    }
                }

                return img_NewBuffer;
            }
            catch (Exception img_ex)
            {
                Logger.LogException(img_ex);
                throw;
            }
        }

        /// <summary>
        /// IMs the g_ histogram equalization.
        /// </summary>
        /// <param name="img_OldImg">The img_ old img.</param>
        /// <returns>image buffer after equalization</returns>
        public static Entities.IMG_Image IMG_HistogramEqualization(Entities.IMG_Image img_OldImg)
        {
            try
            {
                int img_HeightR = 256;
                int img_HeightG = 256;
                int img_HeightB = 256;
                Array.Clear(ImgRedValues, 0, img_HeightR);
                Array.Clear(ImgGreenValues, 0, img_HeightG);
                Array.Clear(ImgBlueValues, 0, img_HeightB);
                IMG_CalculateHistogramValues(img_OldImg);
                int img_PixelSumR = 0, img_PixelSumG = 0, img_PixelSumB = 0;
                float img_PixelProbR = 0, img_PixelProbG = 0, img_PixelProbB = 0;
                float img_total;
                Entities.IMG_Image img_NewImg = new Entities.IMG_Image(img_OldImg.Width, img_OldImg.Height);
                int img_Width = img_OldImg.Width;
                int img_Height = img_OldImg.Height;
                img_total = img_Width * img_Height;
                for (int i = 0; i < img_HeightR; i++)
                {
                    img_PixelSumR += ImgRedValues[i];
                    img_PixelProbR = img_PixelSumR / img_total;
                    ImgRedValues[i] = (int)(img_PixelProbR * (img_HeightR - 1));
                }

                for (int i = 0; i < img_HeightG; i++)
                {
                    img_PixelSumG += ImgGreenValues[i];
                    img_PixelProbG = img_PixelSumG / img_total;
                    ImgGreenValues[i] = (int)(img_PixelProbG * (img_HeightG - 1));
                }

                for (int i = 0; i < img_HeightB; i++)
                {
                    img_PixelSumB += ImgBlueValues[i];
                    img_PixelProbB = img_PixelSumB / img_total;
                    ImgBlueValues[i] = (int)(img_PixelProbB * (img_HeightB - 1));
                }

                for (int i = 0; i < img_total; i++)
                {
                    int img_NewR = ImgRedValues[(int)img_OldImg.ImagePixels[i].Red];
                    int img_NewG = ImgGreenValues[(int)img_OldImg.ImagePixels[i].Green];
                    int img_NewB = ImgBlueValues[(int)img_OldImg.ImagePixels[i].Blue];
                    img_NewImg.ImagePixels[i] = new Entities.Pixel(img_NewR, img_NewG, img_NewB);
                }

                IMG_CalculateHistogramValues(img_NewImg);
                return img_NewImg;
            }
            catch (Exception img_ex)
            {
                Logger.LogException(img_ex);
                throw;
            }
        }

        /// <summary>
        /// IMs the g_ histogram matching.
        /// </summary>
        /// <param name="img_OldImg">The img_ old img.</param>
        /// <param name="img_MatchingIMg">The img_ matching I mg.</param>
        /// <returns>the new buffer matched to the input image</returns>
        public static Entities.IMG_Image IMG_HistogramMatching(Entities.IMG_Image img_OldImg, Entities.IMG_Image img_MatchingIMg)
        {
            try
            {
                int img_HeightR = 256;
                int img_HeightG = 256;
                int img_HeightB = 256;
                int[] redValuesOld = new int[img_HeightR];
                int[] greenValuesOld = new int[img_HeightG];
                int[] blueValuesOld = new int[img_HeightB];
                Array.Clear(ImgRedValues, 0, img_HeightR);
                Array.Clear(ImgGreenValues, 0, img_HeightG);
                Array.Clear(ImgBlueValues, 0, img_HeightB);
                IMG_CalculateHistogramValues(img_OldImg);
                int img_PixelSumR = 0, img_PixelSumG = 0, img_PixelSumB = 0;
                float img_PixelProbR = 0, img_PixelProbG = 0, img_PixelProbB = 0;
                float img_total;
                Entities.IMG_Image img_NewImg = new Entities.IMG_Image(img_OldImg.Width, img_OldImg.Height);
                int img_Width = img_OldImg.Width;
                int img_Height = img_OldImg.Height;
                img_total = img_Width * img_Height;
                for (int i = 0; i < img_HeightR; i++)
                {
                    img_PixelSumR += ImgRedValues[i];
                    img_PixelProbR = img_PixelSumR / img_total;
                    ImgRedValues[i] = (int)(img_PixelProbR * (img_HeightR - 1));
                }

                imgRedValues.CopyTo(redValuesOld, 0);
                for (int i = 0; i < img_HeightG; i++)
                {
                    img_PixelSumG += ImgGreenValues[i];
                    img_PixelProbG = img_PixelSumG / img_total;
                    ImgGreenValues[i] = (int)(img_PixelProbG * (img_HeightG - 1));
                }

                imgGreenValues.CopyTo(greenValuesOld, 0);
                for (int i = 0; i < img_HeightB; i++)
                {
                    img_PixelSumB += ImgBlueValues[i];
                    img_PixelProbB = img_PixelSumB / img_total;
                    ImgBlueValues[i] = (int)(img_PixelProbB * (img_HeightB - 1));
                }

                imgBlueValues.CopyTo(blueValuesOld, 0);
                Array.Clear(ImgRedValues, 0, img_HeightR);
                Array.Clear(ImgGreenValues, 0, img_HeightG);
                Array.Clear(ImgBlueValues, 0, img_HeightB);
                IMG_CalculateHistogramValues(img_MatchingIMg);
                img_PixelSumR = 0;
                img_PixelSumG = 0;
                img_PixelSumB = 0;
                img_PixelProbR = 0;
                img_PixelProbG = 0;
                img_PixelProbB = 0;
                img_Width = img_MatchingIMg.Width;
                img_Height = img_MatchingIMg.Height;
                img_total = img_Width * img_Height;
                for (int i = 0; i < img_HeightR; i++)
                {
                    img_PixelSumR += ImgRedValues[i];
                    img_PixelProbR = img_PixelSumR / img_total;
                    ImgRedValues[i] = (int)(img_PixelProbR * (img_HeightR - 1));
                }

                for (int i = 0; i < img_HeightG; i++)
                {
                    img_PixelSumG += ImgGreenValues[i];
                    img_PixelProbG = img_PixelSumG / img_total;
                    ImgGreenValues[i] = (int)(img_PixelProbG * (img_HeightG - 1));
                }

                for (int i = 0; i < img_HeightB; i++)
                {
                    img_PixelSumB += ImgBlueValues[i];
                    img_PixelProbB = img_PixelSumB / img_total;
                    ImgBlueValues[i] = (int)(img_PixelProbB * (img_HeightB - 1));
                }

                int valsmaller = 0;
                int vallarger = 255;
                for (int i = 0; i < redValuesOld.Length; i++)
                {
                    for (int j = 0; j < imgRedValues.Length; j++)
                    {
                        if (imgRedValues[j] < redValuesOld[i])
                        {
                            valsmaller = j;
                        }
                        else if (imgRedValues[j] == redValuesOld[i])
                        {
                            valsmaller = j;
                            break;
                        }
                        else if (imgRedValues[j] > redValuesOld[i])
                        {
                            vallarger = j;
                            break;
                        }
                    }

                    if (Math.Abs(imgRedValues[vallarger] - redValuesOld[i]) > Math.Abs(redValuesOld[i] - imgRedValues[valsmaller]))
                    {
                        redValuesOld[i] = valsmaller;
                    }
                    else
                    {
                        redValuesOld[i] = vallarger;
                    }

                    valsmaller = 0;
                    vallarger = 255;
                }

                valsmaller = 0;
                vallarger = 255;
                for (int i = 0; i < greenValuesOld.Length; i++)
                {
                    for (int j = 0; j < imgGreenValues.Length; j++)
                    {
                        if (imgGreenValues[j] < greenValuesOld[i])
                        {
                            valsmaller = j;
                        }
                        else if (imgGreenValues[j] == greenValuesOld[i])
                        {
                            valsmaller = j;
                            break;
                        }
                        else if (imgGreenValues[j] > greenValuesOld[i])
                        {
                            vallarger = j;
                            break;
                        }
                    }

                    if (Math.Abs(imgGreenValues[vallarger] - greenValuesOld[i]) > Math.Abs(greenValuesOld[i] - imgGreenValues[valsmaller]))
                    {
                        greenValuesOld[i] = valsmaller;
                    }
                    else
                    {
                        greenValuesOld[i] = vallarger;
                    }

                    valsmaller = 0;
                    vallarger = 255;
                }

                valsmaller = 0;
                vallarger = 255;
                for (int i = 0; i < blueValuesOld.Length; i++)
                {
                    for (int j = 0; j < imgBlueValues.Length; j++)
                    {
                        if (imgBlueValues[j] < blueValuesOld[i])
                        {
                            valsmaller = j;
                        }
                        else if (imgBlueValues[j] == blueValuesOld[i])
                        {
                            valsmaller = j;
                            break;
                        }
                        else if (imgBlueValues[j] > blueValuesOld[i])
                        {
                            vallarger = j;
                            break;
                        }
                    }

                    if (Math.Abs(imgBlueValues[vallarger] - blueValuesOld[i]) > Math.Abs(blueValuesOld[i] - imgBlueValues[valsmaller]))
                    {
                        blueValuesOld[i] = valsmaller;
                    }
                    else
                    {
                        blueValuesOld[i] = vallarger;
                    }

                    valsmaller = 0;
                    vallarger = 255;
                }

                img_Width = img_OldImg.Width;
                img_Height = img_OldImg.Height;
                int index = img_Width * img_Height;
                for (int i = 0; i < index; i++)
                {
                    int img_NewR = redValuesOld[(int)img_OldImg.ImagePixels[i].Red];
                    int img_NewG = greenValuesOld[(int)img_OldImg.ImagePixels[i].Green];
                    int img_NewB = blueValuesOld[(int)img_OldImg.ImagePixels[i].Blue];
                    img_NewImg.ImagePixels[i] = new Entities.Pixel(img_NewR, img_NewG, img_NewB);
                }

                IMG_CalculateHistogramValues(img_NewImg);
                return img_NewImg;
            }
            catch (Exception img_ex)
            {
                Logger.LogException(img_ex);
                throw;
            }
        }
    }
}
